// 因为客户端不可能有多个用户同时访问，我们可以不考虑它的并发，链接消耗资源等问题，使用原始的socket编程
// 客户端也不需要写啥头文件，只需要联合时候服务器的头文件即可
#include <iostream>
#include <cstdlib>
#include <string>
#include <vector>
#include <unordered_map>
#include <functional>
#include <ctime>
#include <thread>

#include <unistd.h>     // 包含 close() 函数（用于关闭套接字）
#include <sys/socket.h> // 核心套接字函数和数据结构，如 socket(), bind(), connect(), accept(), send(), recv()
#include <netinet/in.h> // 包含 IP 地址结构，如 struct sockaddr_in, IPPROTO_TCP, INADDR_ANY
#include <arpa/inet.h>  // 包含地址转换函数，如 inet_addr(), inet_pton(), inet_ntop()
#include <netdb.h>      // 用于主机名查询，如 gethostbyname(), getaddrinfo()

#include "User.hpp"
#include "Group.hpp"
#include "public.hpp"
#include "json.hpp"
#include "db.h"
using json = nlohmann::json;

// 用户信息
User g_user;
// 用户好友信息
std::vector<User> g_userfriendlist;
// 记录用户所在组的信息
std::vector<Group> g_usergrouplist;
// 我们肯定是需要两个线程的，因为如果只有一个线程，用户不输入就会阻塞在cin，还接受不了信息
void readTaskHanlder(int clinetfd);
// 获取当前时间
std::string getCurrentTime();
// 聊天主菜单(用户登录成功后就开启这个主菜单)
void chatMenu(int clientfd);
// 展示登录成功后所有业务
void showService();

void help(int clientfd = 0, std::string str = "");
void oneChat(int clientfd, std::string str);
void addFriend(int clientfd, std::string str);
void createGroup(int clientfd, std::string str);
void joinGroup(int clientfd, std::string str);
void groupChat(int clientfd, std::string str);
void changeRole(int clientfd, std::string str);
void quit(int clientfd = 0, std::string str = "");

// 登录菜单成功后，就可以选择各种各样的业务执行了
// 注意，我们自己的信息有上面的几个全局变量给出，所以以下这几个格式只是给出对端的id即可。
std::unordered_map<std::string, std::string> serviceMap = {
    {"quit", "will be quit client"},
    {"help", "will be show all Service and format"},
    {"oneChat", "一对一聊天 : 格式:friendid:message"},
    {"addFriend", "添加好友 : 格式:friendid"},
    {"createGroup", "创建群聊 : 格式:groupname:groupdesc"},
    {"joinGroup", "加入群聊 : 格式:groupid"},
    {"groupChat", "群聊 : 格式:groupid:msg"},
    {"changeRole", "设置群成员身份 : 格式:groupid:toid:newrole"}};

std::unordered_map<std::string, std::function<void(int, std::string)>> serviceHanlderMap = {
    {"help", help},
    {"oneChat", oneChat},
    {"addFriend", addFriend},
    {"createGroup", createGroup},
    {"joinGroup", joinGroup},
    {"groupChat", groupChat},
    {"changeRole", changeRole},
    {"quit", quit}};

void readTaskHanlder(int clientfd)
{
    // 接收客户端发来的信息，注意这是一个线程，他只负责接收，所以我们之间写for循环写死即可
    for (;;)
    {
        char buffer[1024];
        int len = recv(clientfd, buffer, sizeof buffer - 1, 0);
        if (len == -1 && len == 0)
        {
            std::cout << "recv error or opposite close" << std::endl;
            exit(-1);
        }

        json requset = json::parse(buffer);
        int msgid = requset["msgid"].get<int>();
        if (msgid == ONE_CHAT_MSG)
        {
            std::cout << "from id : " << requset["id"].get<int>() << " from : " << requset["from"].get<std::string>() << " meg : " << requset["msg"].get<std::string>() << std::endl;
            continue;
        }

        if (msgid == GROUP_CHAT_MSG)
        {
            std::cout << "groupid : " << requset["fromgroupid"].get<int>() << " " << "from id : " << requset["fromid"].get<int>() << " " << "from name : " << requset["fromname"].get<std::string>() << "msg : " << requset["msg"].get<int>() << std::endl;
            continue;
        }
    }
}

std::string getCurrentTime()
{
    std::string str = "empty";
    return str;
}

void help(int clientfd, std::string str)
{
    for (auto &e : serviceMap)
    {
        std::cout << e.first << "," << e.second << std::endl;
    }

    std::cout << "任务与任务需要的信息应该由“,”隔开,执行任务所需要的信息应该由“:”隔开" << std::endl;
    std::cout << "example : oneChat,145144:hello" << std::endl;
}

//{"oneChat", "一对一聊天 : 格式:friendid:message"},
void oneChat(int clientfd, std::string str)
{
    std::cout << str << std::endl;
    int index = str.find(':');
    if (index == -1)
    {
        std::cerr << "the message of oneChat enter error,please check you enter" << std::endl;
        return;
    }
    else
    {
        int toid = atoi(str.substr(0, index).c_str());
        std::string buffer = str.substr(index + 1);

        std::cout << "toid : " << toid << "buffer : " << buffer << std::endl;

        // 构建json字符串发送
        json js;
        js["msgid"] = ONE_CHAT_MSG;
        js["id"] = g_user.getId();
        js["from"] = g_user.getName();
        js["to"] = toid;
        js["msg"] = buffer;
        js["time"] = getCurrentTime();
        std::string sendjs = js.dump();

        int n = send(clientfd, sendjs.c_str(), strlen(sendjs.c_str()) + 1, 0);
        if (n == -1)
            std::cerr << "oneChat send message error" << sendjs << std::endl;
    }
}

//{"addFriend", "添加好友 : 格式:friendid"}
void addFriend(int clientfd, std::string str)
{
    int friendid = atoi(str.c_str());

    json js;
    js["msgid"] = ADD_FERIEND_MSG;
    js["id"] = g_user.getId();
    js["friendid"] = friendid;
    js["time"] = getCurrentTime();
    std::string sendjs = js.dump();

    int n = send(clientfd, sendjs.c_str(), strlen(sendjs.c_str()) + 1, 0);
    if (n == -1)
        std::cerr << "addFriend send message error" << sendjs << std::endl;
}

// {"createGroup", "创建群聊 : 格式:groupname:groupdesc"},
void createGroup(int clientfd, std::string str)
{
    int index = str.find(':');
    if (index == -1)
    {
        std::cerr << "the message of createGroup enter error,please check you enter" << std::endl;
        return;
    }
    else
    {
        std::string groupname = str.substr(0, index);
        std::string groupdesc = str.substr(index + 1);

        json js;
        js["msgid"] = CREATE_GROUP_MSG;
        js["id"] = g_user.getId();
        js["groupname"] = groupname;
        js["groupdesc"] = groupdesc;
        js["time"] = getCurrentTime();
        std::string sendjs = js.dump();

        int n = send(clientfd, sendjs.c_str(), strlen(sendjs.c_str()) + 1, 0);
        if (n == -1)
            std::cerr << "createGroup send message error" << sendjs << std::endl;
    }
}

//  {"jionGroup", "加入群聊 : 格式:groupid"},
void joinGroup(int clientfd, std::string str)
{
    int groupid = atoi(str.c_str());

    json js;
    js["msgid"] = JOIN_GROUP_MSG;
    js["id"] = g_user.getId();
    js["groupid"] = groupid;
    js["time"] = getCurrentTime();
    std::string sendjs = js.dump();

    int n = send(clientfd, sendjs.c_str(), strlen(sendjs.c_str()) + 1, 0);
    if (n == -1)
        std::cerr << "jionGroup send message error" << sendjs << std::endl;
}

//  {"groupChat", "群聊 : 格式:groupid:msg"},
void groupChat(int clientfd, std::string str)
{
    int index = str.find(':');
    if (index == -1)
    {
        std::cerr << "the message of groupChat enter error,please check you enter" << std::endl;
        return;
    }
    else
    {
        int groupid = atoi(str.substr(0, index).c_str());
        std::string msg = str.substr(index + 1);

        json js;
        js["msgid"] = GROUP_CHAT_MSG;
        js["id"] = g_user.getId();
        js["name"] = g_user.getName();
        js["groupid"] = groupid;
        js["msg"] = msg;
        js["time"] = getCurrentTime();
        std::string sendjs = js.dump();

        int n = send(clientfd, sendjs.c_str(), strlen(sendjs.c_str()) + 1, 0);
        if (n == -1)
            std::cerr << "groupChat send message error" << sendjs << std::endl;
    }
}

// {"changeRole", "设置群成员身份 : 格式:groupid:toid:newrole"}
void changeRole(int clientfd, std::string str)
{
    int index1 = str.find(':');
    int index2 = str.rfind(':');
    if (index1 == -1 || index2 == -2 || index1 == index2)
    {
        std::cerr << "the message of changeRole enter error,please check you enter" << std::endl;
        return;
    }
    else
    {
        int groupid = atoi(str.substr(0, index1).c_str());
        int toid = atoi(str.substr(index1 + 1, index2).c_str());
        std::string newrole = str.substr(index2 + 1);

        json js;
        js["msgid"] = CHANGE_ROLE_MSG;
        js["id"] = g_user.getId();
        js["groupid"] = groupid;
        js["toid"] = toid;
        js["newrole"] = newrole;
        js["time"] = getCurrentTime();
        std::string sendjs = js.dump();

        int n = send(clientfd, sendjs.c_str(), strlen(sendjs.c_str()) + 1, 0);
        if (n == -1)
            std::cerr << "changeRole send message error" << sendjs << std::endl;
    }
}

void quit(int clientfd, std::string str)
{
    std::cout << "Bye Bye" << std::endl;
    exit(0);
}

void chatMenu(int clientfd)
{
    help(); // 先给用户展示一下业务,然后再做出定夺

    // 到这个地方肯定是一直for循环在菜单这里
    for (;;)
    {
        char buffer[1024];
        std::string str = buffer;
        std::getline(std::cin, str);

        std::string service;
        std::string needMsg;

        int index = str.find(',');
        if (index == -1)
        {
            service = str; // 如果没有,说明整个没有依赖的参数，可以断定一定是help函数,所以直接赋值即可
        }
        else
        {
            service = str.substr(0, index);
            needMsg = str.substr(index + 1);
        }

        auto it = serviceHanlderMap.find(service);
        if (it != serviceHanlderMap.end())
            it->second(clientfd, needMsg);
        else
            std::cout << "Input format error,Please check input format" << std::endl;
    }
}

// 显示当前登录成功的用户的信息
void ShowOnlineUserMessage()
{
    std::cout << std::endl;
    std::cout << "===========================================" << std::endl;

    std::cout << "================LOGIN  USER================" << std::endl;
    std::cout << "《current user》" << "id: " << g_user.getId() << " " << "name: " << g_user.getName() << std::endl;

    std::cout << "================FRIEND LIST================" << std::endl;

    if (!g_userfriendlist.empty())
    {
        for (auto &e : g_userfriendlist)
        {
            std::cout << "friendid: " << e.getId() << " " << "friendname: "
                      << e.getName() << " " << "onlinestate: " << e.getState() << std::endl;
        }
    }

    std::cout << "================GROUP  LIST================" << std::endl;

    if (!g_usergrouplist.empty())
    {
        for (auto &e : g_usergrouplist)
        {
            std::cout << "groupid: " << e.getId() << "groupname: "
                      << e.getGroupname() << "groupdesc: " << e.getGroupdesc() << std::endl;
        }
    }

    std::cout << "===========================================" << std::endl;
    std::cout << std::endl;
}

void Uages()
{
    std::cout << "Uages : ./Chatclient ip port" << std::endl;
}

// 巧记，命令行参数就是 （arg）c + （arg）v
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Uages();
        exit(0);
    }

    std::string ip = argv[1];
    uint16_t port = atoi(argv[2]);

    // 创建套接字
    int clientfd = socket(AF_INET, SOCK_STREAM, 0);
    if (clientfd == -1)
    {
        std::cerr << "errno create clientfd failed!" << std::endl;
        close(clientfd);
        exit(-1);
    }

    sockaddr_in saddr;
    memset(&saddr, 0, sizeof(sockaddr_in));

    // 设置saddr结构体准备链接，  要把port和ip主机信息转网络
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(port);
    saddr.sin_addr.s_addr = inet_addr(ip.c_str());

    if (connect(clientfd, (const sockaddr *)&saddr, sizeof(sockaddr_in)) == -1)
    {
        std::cerr << "errno socket connect failed!" << std::endl;
        close(clientfd);
        exit(-1);
    }

    // 这就是客户端socket要干的事情，要创建套接字，要connect链接，而服务器就要设置监听了
    // （因为本项目用的是muduo网络库，所以服务器就不用管了）

    // 正式开始业务，首先应该是登录业务
    for (;;)
    {
        std::cout << "==============choice      service==============" << std::endl;
        std::cout << "==============choice 1 :    Login==============" << std::endl;
        std::cout << "==============choice 2 : register==============" << std::endl;
        std::cout << "==============choice 3 :     quit==============" << std::endl;

        int choice;
        std::cin >> choice;
        std::cin.get();
        // 这边一定要清楚输入缓冲区，不然后续会读到换行

        switch (choice)
        {
        case 1:
        {
            // 登录业务就显得相对比较复杂了
            int id;
            std::string password;
            std::cout << "Logining! please provide you" << std::endl;
            std::cout << "import id : " << std::endl;
            std::cin >> id;
            std::cin.ignore();
            std::cout << "import password : " << std::endl;
            std::cin >> password;
            std::cin.ignore();

            // 组装json对象
            json loginjs;
            loginjs["msgid"] = LOGIN_MSG;
            loginjs["id"] = id;
            loginjs["password"] = password;
            std::string loginstr = loginjs.dump();

            ssize_t len = send(clientfd, loginstr.c_str(), strlen(loginstr.c_str()) + 1, 0);
            if (len == -1)
            {
                std::cerr << "send Message of Login is failed" << std::endl;
            }
            {
                char buffer[1024];
                // 解析成C型字符串
                ssize_t n = recv(clientfd, buffer, sizeof buffer - 1, 0);
                if (n == -1)
                {
                    std::cerr << "recv Message of Login from Server is failed" << std::endl;
                }
                else
                {
                    // 现在接收到服务器返回的json字符串，我们还是得分类讨论
                    json responce = json::parse(buffer);
                    if (responce["error"].get<int>() == 1 || responce["error"].get<int>() == 2)
                    {
                        std::cout << responce["errorMeg"].get<std::string>() << std::endl;
                    }
                    else
                    {
                        std::cout << std::endl;
                        // 正式登陆成功，这里要解析的东西就比较多了
                        std::cout << "id : " << responce["id"].get<int>() << " " << "name : " << responce["name"] << " " << responce["LoginMeg"] << std::endl;

                        // 设置全局变量
                        g_user.setId(responce["id"].get<int>());
                        g_user.setName(responce["name"]);

                        if (responce.find("FriendMsg") != responce.end())
                        {
                            std::vector<std::string> FriendMsg = responce["FriendMsg"];
                            // 由于FriendMsg里面还是json字符串，所以还要反序列化
                            for (auto &e : FriendMsg)
                            {
                                json friendjs = json::parse(e);

                                User friends;
                                friends.setId(friendjs["id"]);
                                friends.setName(friendjs["name"]);
                                friends.setState(friendjs["state"]);

                                g_userfriendlist.push_back(friends);
                            }
                        }

                        if (responce.find("GroupMeg") != responce.end())
                        {
                            std::vector<std::string> GroupMeg = responce["GroupMeg"];

                            for (auto &e : GroupMeg)
                            {
                                json groupjs = json::parse(e);

                                Group group;
                                group.setId(groupjs["groupid"]);
                                group.setGroupname(groupjs["groupname"]);
                                group.setGroupdesc(groupjs["groupdesc"]);

                                g_usergrouplist.push_back(group);
                            }
                        }
                        // 至此，全局变量已经设置完成

                        ShowOnlineUserMessage();

                        // json : msgid, id , from(发起者的名字) , to , meg , time;
                        if (responce.find("OfflineMessage") != responce.end())
                        {
                            // 打印所有离线信息
                            std::vector<std::string> OfflineMeg = responce["OfflineMessage"];

                            for (auto &e : OfflineMeg)
                            {
                                // 这里注意了，e应该是被序列化的json字符串，所以这里进行反序列一下
                                json offline_js = json::parse(e);

                                if (offline_js["msgid"].get<int>() == ONE_CHAT_MSG)
                                {
                                    std::cout << "[个人聊天]from : " << offline_js["from"] << " "
                                              << "He id : " << offline_js["id"].get<int>() << " "
                                              << "include Message: " << offline_js["meg"] << " "
                                              << " time : " << offline_js["time"] << std::endl;
                                }
                                else if (offline_js["msgid"].get<int>() == GROUP_CHAT_MSG)
                                {
                                    std::cout << "[群聊天]groupid : " << offline_js["fromgroupid"].get<int>() << " "
                                              << "from id : " << offline_js["fromid"].get<int>() << " " 
                                              <<"from name : "<< offline_js["fromname"].get<std::string>() << " "
                                              <<"msg : " << offline_js["msg"].get<int>() << " "
                                             << offline_js["time"] << std::endl;
                                }
                            }
                        }
                        // 登录成功后需要，开启读线程专门负责读，不然整个客户端可能阻塞在cin输入
                        // 设置分离线程，让操作系统管理该线程的释放(这就是CPP的reakTask类由C++11引入)
                        std::thread readTask(readTaskHanlder, clientfd);
                        readTask.detach();
                        // 开启聊天菜单
                        chatMenu(clientfd);
                    }
                }
            }
        }
        break;
        case 2:
        {
            std::string name;
            std::string password;
            std::cout << "please set you" << std::endl;
            std::cout << "set name : " << std::endl;
            std::getline(std::cin, name);
            std::cout << "set password" << std::endl;
            std::getline(std::cin, password);

            json js;
            js["msgid"] = REG_MSG;
            js["name"] = name;
            js["password"] = password;
            std::string requset = js.dump();

            // string 转为 char*
            int len = send(clientfd, requset.c_str(), strlen(requset.c_str()) + 1, 0);
            if (len == -1)
            {
                // cout // cin // cerr
                std::cerr << "send message failed" << std::endl;
            }
            else
            {
                char buffer[1024];
                ssize_t n = recv(clientfd, buffer, sizeof(buffer) - 1, 0);
                if (n == -1)
                {
                    std::cerr << "recv no every message" << std::endl;
                }
                else
                {
                    // 至此，我们收到了一个被序列化发过来的字符串
                    json recvMeg = json::parse(buffer);

                    if (recvMeg["error"].get<int>() == 0)
                        std::cout << recvMeg["returnMessage"] << recvMeg["id"].get<int>() << std::endl;
                    else
                        std::cout << recvMeg["errorMessaege"] << std::endl;
                }
            }
        }
        break;
        case 3:
        {
            std::cout << "Bye Bye" << std::endl;
            exit(0);
        }
        break;
        default:
        {
            std::cerr << "choice errno please restart choice" << std::endl;
        }
        break;
        }
    }
    return 0;
}